package com.ljx.seckill.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljx.seckill.constant.SeckillConstant;
import com.ljx.seckill.domain.entity.Goods;
import com.ljx.seckill.domain.entity.Order;
import com.ljx.seckill.mapper.GoodsMapper;
import com.ljx.seckill.service.GoodsService;
import com.ljx.seckill.service.OrderService;
import com.ljx.seckill.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @Author: LiJunXi
 * @Description:
 * @Date: 2025-01-09  22:34
 */
@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements GoodsService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private OrderService orderService;

    @Override
    public void seckill(Long goodId, Long userId) {
//        fun1(goodId);
//        fun2(goodId);
//        fun3(goodId);
        fun4(goodId, userId);
    }

    private void fun4(Long goodId, Long userId) {
        String key = SeckillConstant.LOCK_PREFIX + goodId;
        RedisUtilV4 redisUtilV4 = new RedisUtilV4(stringRedisTemplate);
        try {
            Boolean isLock = redisUtilV4.lock(key, 30, TimeUnit.SECONDS);
            if (!isLock) {
                return;
            }
            if (checkParams(goodId)) {
                return;
            }
            lambdaUpdate().eq(Goods::getId, goodId)
                    .setSql("stock = stock - 1")
                    .gt(Goods::getStock, 0)
                    .update();

            new Thread(() -> {
                orderService.save(new Order(new SnowFlakeIdWorker(1).nextId(), userId, goodId));
            }).start();

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            redisUtilV4.unlock(key);
        }
    }

    private boolean checkParams(Long goodId) {
        Goods goods = getById(goodId);
        if (goods.getStock() <= 0) {
            return true;
        }
        return false;
    }

    private void fun3(Long goodId) {
        String key = SeckillConstant.LOCK_PREFIX + goodId;
        RedisUtilV3 redisUtilV3 = new RedisUtilV3(stringRedisTemplate);
        try {
            Boolean isLock = redisUtilV3.lock(key, 30, TimeUnit.SECONDS);
            if (!isLock) {
                return;
            }
            lambdaUpdate().eq(Goods::getId, goodId)
                    .setSql("stock = stock - 1")
                    .gt(Goods::getStock, 0)
                    .update();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            Boolean unlock = redisUtilV3.unlock(key);
            if (!unlock) {
                throw new RuntimeException("释放锁出错");
            }
        }
    }

    private void fun2(Long goodId) {
        String key = SeckillConstant.LOCK_PREFIX + goodId;
        RedisUtilV2 redisUtilV2 = new RedisUtilV2(stringRedisTemplate);
        try {
            Boolean isLock = redisUtilV2.lock(key, 30, TimeUnit.SECONDS);
            if (!isLock) {
                return;
            }
            lambdaUpdate().eq(Goods::getId, goodId)
                    .setSql("stock = stock - 1")
                    .gt(Goods::getStock, 0)
                    .update();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            Boolean unlock = redisUtilV2.unlock(key);
            if (!unlock) {
                throw new RuntimeException("释放锁出错");
            }
        }
    }

    private void fun1(Long goodId) {
        String key = SeckillConstant.LOCK_PREFIX + goodId;
        RedisUtilV1 redisUtilV1 = new RedisUtilV1(stringRedisTemplate);
        try {
            Boolean isLock = redisUtilV1.lock(key, 30, TimeUnit.SECONDS);
            if (!isLock) {
                return;
            }
            lambdaUpdate().eq(Goods::getId, goodId)
                    .setSql("stock = stock - 1")
                    .gt(Goods::getStock, 0)
                    .update();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            Boolean unlock = redisUtilV1.unlock(key);
            if (!unlock) {
                throw new RuntimeException("释放锁出错");
            }
        }
    }
}
